package Logic;

import Core.Log4j2;
import Core.NetData;
import Core.ProtoCenter;
import com.microgame.grpc.CheckUserServiceGrpc;
import com.microgame.grpc.UserIdCheckRequest;
import com.microgame.grpc.UserIdCheckResponse;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelId;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import microgame.airplane.proto.Login;
import microgame.airplane.proto.MsgTypeOuterClass;
import microgame.airplane.proto.ProtoPacker;
import microgame.toolset.serverconfig.ServerConfig;
import org.apache.logging.log4j.core.tools.picocli.CommandLine;
import java.util.Hashtable;
import java.util.Locale;

public class ClientManager implements ProtoProcessorInterface {
    private static ClientManager _instance;

    private Hashtable<String, Client> clientMap = new Hashtable<>();

    private ClientManager() {
    }

    public static ClientManager Instance() {
        if (_instance == null) {
            _instance = new ClientManager();
        }
        return _instance;
    }

    public void Init() {
        RegisterProtoProcessor();
    }

    public void Add(String id, Client c) {
        if (c != null) {
            clientMap.put(id, c);
        }
    }

    public void Remove(String id) {
            clientMap.remove(id);
    }

    public Client GetClient(String id) {
        return clientMap.get(id);
    }

    public void ProtoClientLogin(NetData nd) throws Exception {
        Login.cLoginReq loginReq = Login.cLoginReq.parseFrom(nd.GetData());
        //uid是user id的简称，客户端发过来的是用户系统中分配的uniqueid，所以一定是唯一的
        String uid = loginReq.getUid();
        Log4j2.LOGGER.info("[Proto:] Check user id, uid = " + uid);

        //这是用gRPC从服务器验证uid的存在性
        try {
            String userServeRpcHost = ServerConfig.Instance().GetUserServeRpcHost();
            int userServeRpcPort = ServerConfig.Instance().GetUserServeRpcPort();
            ManagedChannel channel = ManagedChannelBuilder.forAddress(userServeRpcHost, userServeRpcPort).usePlaintext().build();
            CheckUserServiceGrpc.CheckUserServiceBlockingStub stub = CheckUserServiceGrpc.newBlockingStub(channel);
            UserIdCheckResponse response = stub.validateUser(UserIdCheckRequest.newBuilder().setUid(uid).build());
            Log4j2.LOGGER.info("User id check, result = " + response.getResult());
            channel.shutdown();

            if (!response.getResult()) {
                Login.sLoginAck.Builder builder = Login.sLoginAck.newBuilder();
                builder.setCode(-2);
                builder.setDescription("用户ID在系统中不存在.");
                Login.sLoginAck loginAck = builder.build();
                Util.SendMessageThruChannel(MsgTypeOuterClass.MsgType.sLoginAck.getNumber(), nd.GetChannel(), loginAck.toByteArray());
                return;
            }
        }
        catch (io.grpc.StatusRuntimeException e) {
            Log4j2.LOGGER.error(e.toString() + " | Please check grpc server's status.");
        }

        if (clientMap.get(nd.GetClientId()) != null) {
            Log4j2.LOGGER.error("Client with same id (netty) already exist.");
            Log4j2.LOGGER.info("Token = " + uid);

            Login.sLoginAck.Builder builder = Login.sLoginAck.newBuilder();
            builder.setCode(-1);
            builder.setDescription("Alread login.");
            Login.sLoginAck loginAck = builder.build();
            Util.SendMessageThruChannel(MsgTypeOuterClass.MsgType.sLoginAck.getNumber(), nd.GetChannel(), loginAck.toByteArray());
            return;
        }

        //登记新的客户端
        Client client = new Client(nd.GetClientId(), uid, nd.GetChannel());
        clientMap.put(nd.GetClientId(), client);
        Log4j2.LOGGER.info("Add Client.");

        //回发登录成功事件
        Login.sLoginAck.Builder builder = Login.sLoginAck.newBuilder();
        builder.setCode(200);
        builder.setDescription("OK");
        Login.sLoginAck loginAck = builder.build();
        Util.SendMessageThruChannel(MsgTypeOuterClass.MsgType.sLoginAck.getNumber(), nd.GetChannel(), loginAck.toByteArray());
        Log4j2.LOGGER.info("Reponse sLoginAck(200) to client.");
    }

    public void RegisterProtoProcessor() {
        //协议处理
        ProtoCenter.RegisterProtoProcessor(MsgTypeOuterClass.MsgType.cLoginReq.getNumber(), this, "ProtoClientLogin");
    }
}
